
#include <cstdarg>
#include "avatar.h"
#include "creature.h"
#include "display.h"
#include "handler.h"
#include "estring.h"
#include "world.h"

Avatar::Avatar( Socket* socket ) {
  type( TYPE_AVATAR, true );
  status( CONNECTING );
  disconnected( false );
  this->socket( socket );
  name( estring(this->socket()->getFd()) );
  return;
}

Avatar::~Avatar( void ) {
  delete socket();
  return;
}

/******************************************************* Avatar-specific methods ********************************************************/
/*
 * This methods exists basically to consolidate the underlying actions of
 * the various overloads of Avatar::send() so that we can do things like
 * color in a central location.
 */
void Avatar::processOutput( const std::string& src ) { // private
  output( output() + src );
  return;
}

bool Avatar::hasInput( void ) { // public
  return socket()->hasInput();
}

std::string Avatar::getInput( void ) { // public
  return socket()->getInput();
}

bool Avatar::hasOutput( void ) { // public
  return ( output().length() > 0 );
}

void Avatar::flushOutput( void ) { // public
  socket()->send( Display::parseColors( output() + handlers().back()->prompt() ) );
  output( std::string() );
  return;
}

std::string Avatar::listWhoFlags( void ) { // public
  std::string output;
  unsigned long mask = 1;

  for ( unsigned long u = 0; u < sizeof(unsigned long)*8; ++u ) {
    if ( who( mask ) ) {
      output.append( Avatar::getWhoFlagName( mask ) ).append( 1, ' ' );
    }
    mask <<= 1;
  }

  return output;
}

/******************************************************* Overloads of pure virtual methods ********************************************************/
bool Avatar::save( void ) { // public
  try {
    Mysql* mysql = World::Instance().getMysql();
    char query[MAX_BUFFER];

    sprintf( query,
      "UPDATE avatars SET     \
        name = '%s',          \
        level = %d,           \
        adminFlags = %lu,     \
        gechoColor = '%c',    \
        channelFlags = %lu,   \
        whoFlags = %lu,       \
        room = %lu,           \
        poofin = '%s',        \
        poofout = '%s'        \
       WHERE avatarID = %lu   \
       LIMIT 1;",
      Mysql::addslashes(name()).c_str(),
      level(),
      adminFlags(),
      gechoColor(),
      channelFlags(),
      whoFlags(),
      room()->vnum(),
      Mysql::addslashes(poofin()).c_str(),
      Mysql::addslashes(poofout()).c_str(),
      ID()
    );
    mysql->update( query );

  } catch ( MysqlException me ) {
    fprintf( stderr, "Failed to save avatar %lu: %s\n", ID(), me.getMessage().c_str() );
    return false;
  }

  return true;
}

bool Avatar::destroy( void ) { // public
  unsigned long tempID = ID();
  try {
    char query[MAX_BUFFER];

    sprintf( query,
      " UPDATE                \
        avatars               \
        SET active = 0        \
        WHERE avatarID = %lu  \
        LIMIT 1;",
      ID()
    );
    World::Instance().getMysql()->remove( query );
    delete this;

  } catch ( MysqlException me ) {
    fprintf( stderr, "Failed to delete mob %lu: %s\n", tempID, me.getMessage().c_str() );
    return false;
  }
  return true;
}

bool Avatar::load( void ) { // public
  Mysql* mysql = World::Instance().getMysql();
  char query[MAX_BUFFER];
  ROW row;

  sprintf( query, "SELECT * FROM avatars WHERE LOWER(name) = LOWER('%s') LIMIT 1;", Mysql::addslashes(name()).c_str() );
  if ( mysql->select( query ) ) {
    if ( row = mysql->fetch() ) {
      ID( row["avatarID"] );
      name( row["name"] );
      addKeyword( Regex::lower(name()) );
      level( row["level"] );
      adminFlags( row["adminFlags"] );
      gechoColor( row["gechoColor"] );
      channelFlags( row["channelFlags"] );
      whoFlags( row["whoFlags"] );
      room( World::Instance().findRoom( row["room"] ) );
      // Just in case...
      if ( !room() ) {
        room( World::Instance().findRoom( 0 ) );
      }
      room()->add( this );
      poofin( row["poofin"] );
      poofout( row["poofout"] );
      return true;
    }
  }

  return false;
}



/******************************************************* Overloads of virtual methods ********************************************************/
void Avatar::send( const std::string& message ) { // public
  processOutput( message );
  return;
}

void Avatar::send( const char* format, ... ) { // public
  char buffer[MAX_BUFFER];
  va_list args;
  va_start( args, format );
  vsprintf( buffer, format, args );
  va_end( args );
  processOutput( buffer );
  return;
}

bool Avatar::checkPassword( const std::string& attempt ) { // public
  Mysql* mysql = World::Instance().getMysql();
  char query[MAX_BUFFER];
  ROW row;

  memset( query, 0, MAX_BUFFER );
  sprintf( query, "SELECT name FROM avatars WHERE LOWER(name) = LOWER('%s') AND password = PASSWORD('%s') LIMIT 1;",
    Mysql::addslashes(name()).c_str(),
    Mysql::addslashes(attempt).c_str()
  );
  if ( mysql->select( query ) ) {
    if ( row = mysql->fetch() ) {
      if ( row["name"] == name() ) {
        return true;
      }
    }
  }

  return false;
}



/******************************************************* Static methods ********************************************************/
const char* Avatar::getWhoFlagName( const unsigned long& flag ) { // public static
  switch( flag ) {
    case WHO_AFK:   return "AFK";
    case WHO_BUSY:  return "BUSY";
    case WHO_BORED: return "BORED";
    case WHO_DUNCE: return "DUNCE";
    default:        return "unknown";
  }
}

unsigned long Avatar::getWhoFlagType( const std::string& flag ) { // public static
  if ( Regex::strPrefix( flag, "afk" ) ) {
    return WHO_AFK;
  } else if ( Regex::strPrefix( flag, "busy" ) ) {
    return WHO_BUSY;
  } else if ( Regex::strPrefix( flag, "bored" ) ) {
    return WHO_BORED;
  } else if ( Regex::strPrefix( flag, "dunce" ) ) {
    return WHO_DUNCE;
  } else {
    return WHO_UNDEFINED;
  }
}

